#include "linux/module.h"
#include "linux/init.h"
#include "linux/fs.h"
#include "linux/cdev.h"
#include "linux/kdev_t.h"
#include "linux/uaccess.h"
#include "linux/io.h"
#include "linux/poll.h"
#include "linux/signal.h"

static char mem[1024];

struct chrdev
{
    dev_t dev_num;         // 设备号
    struct cdev cdev_test; // cdev
    struct class *class;   // 设备类
    struct device *device; // 设备
    char kbuf[32];
    int flag;
} dev1;

//=================================================
static int open(struct inode *inode, struct file *file)
{
    file->private_data = &dev1;
    printk(KERN_INFO "打开文件\n");
    return 0;
}
static int release(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "关闭文件\n");
    return 0;
}
static ssize_t read(struct file *file, char __user *buff, size_t size, loff_t *off)
{
    loff_t p=*off;
    size_t count=size;
    if (count>1024)
    {
        return -1;
    }
    if (count>1024-p)
    {
        count = 1024-p;//如果要读取的值超出剩余空间则设为最后位置
    }
    if (copy_to_user(buff,mem+p,count))//将mem中的值写入buf,传递到用户空间
    {
        printk(KERN_ERR"传送数据到用户空间失败");
        return -1;  
    }

    printk(KERN_INFO"mem为:%s\n",mem);

    *off+=count;
    return count;
}
static ssize_t write(struct file *file, const char __user *buff, size_t size, loff_t *off)
{
    loff_t p=*off;
    size_t count=size;
    if (p>1024)
    {
        return 0;
    }
    if (count>1024-p)
    {
        count=1024-p;   
    }
    if (copy_from_user(mem+p,buff,count))
    {
           printk(KERN_ERR"传送数据到内核空间失败"); 
           return -1;
    }
    printk("写入的值为:%s\n",mem+p);
    *off=*off+count;
    return count;
}

loff_t llseek(struct file *file, loff_t offset, int whence)
{
    loff_t new_offset; // 新的偏移值
    switch (whence)
    {
    case SEEK_SET: // 起始位置
        if (offset < 0)
            return -EINVAL;
        if (offset > 1024)
            return -EINVAL;
        new_offset = offset;
        break;
    case SEEK_CUR: // 当前位置
        if (file->f_pos + offset > 1024)
            return -EINVAL;
        if (file->f_pos + offset < 0)
            return -EINVAL;
        new_offset = file->f_pos + offset;
        break;
    case SEEK_END: // 最后位置
        if (file->f_pos + offset < 0)
            return -EINVAL;
        new_offset = 1024 + offset;
    default:
        break;
    }
    file->f_pos=new_offset;
    return new_offset;
}

//=================================================

struct file_operations fops_test = {
    .owner = THIS_MODULE,
    .open = open,
    .release = release,
    .read = read,
    .write = write,
    .llseek = llseek,
};

static int __init my_driver_init(void)
{
    alloc_chrdev_region(&dev1.dev_num, 0, 1, "test");
    printk(KERN_INFO "主设备号为:%d,次设备号为:%d\n", MAJOR(dev1.dev_num), MINOR(dev1.dev_num));
    cdev_init(&dev1.cdev_test, &fops_test); // 设备初始化cdev结构体和file_operations关联
    dev1.cdev_test.owner = THIS_MODULE;
    cdev_add(&dev1.cdev_test, dev1.dev_num, 1); // 添加设备
    dev1.class = class_create(THIS_MODULE, "test_class");
    dev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "test_drive");
    return 0;
}

static void __exit my_driver_exit(void)
{
    device_destroy(dev1.class, dev1.dev_num);
    class_destroy(dev1.class);
    cdev_del(&dev1.cdev_test);
    unregister_chrdev_region(dev1.dev_num, 1);
}

module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("逮就捕");